/**
 * Ship's that everyone has in the game
 */

package edu.gatech.spacetraderteam3.model;

import java.io.Serializable;

import edu.gatech.spacetraderteam3.debug.Debug;

/**
 * Ship class that has inventory, shields, health etc
 * 
 * @author Randal
 * @version 0.1
 */
public class Ship implements Serializable {

    /**
     * The ship's serial version UID for saving
     */
    private static final long serialVersionUID = -7503487103524217059L;

    /**
     * Integer values taht dictate the ship's status
     */
    private int health, shipDefense, maxWeight, inventoryWeight = 0, maxFuel,
            currentFuel, shields, weapons, armors, maxShields;

    /**
     * The name and classification of this ship
     */
    private String name, type = "Ship";
    /**
     * This equals 100
     */
    private static final double ONEHUNDREDDOUBLE = 100.0;
    /**
     * Miss rate in battle
     */
    private static final double MISS_RATE = 0.15;
    
    /**
     * Ship constructor. Sets a lot of stuff
     * 
     * @param name
     *            Name of the ship
     * @param health
     *            Health of the ship
     * @param shipDefense
     *            The Ship's defense
     * @param maxWeight
     *            The maximum cargo weight the ship can take off with
     * @param maxFuel
     * @param weaponLevel
     * @param armorLevel
     * @param shieldLevel
     */
    protected Ship(String name, int health, int shipDefense, int maxWeight, int maxFuel,
            int weaponLevel, int armorLevel, int shieldLevel) {
        this.name = name;
        this.health = health;
        this.shipDefense = shipDefense;
        this.maxWeight = maxWeight;
        this.weapons = weaponLevel;
        this.armors = armorLevel;
        this.maxShields = shieldLevel;
        this.maxFuel = maxFuel;
        this.currentFuel = maxFuel;
        this.shields = shieldLevel;
    }

    /**
     * Constructor that gives a lot of default values.
     * 
     * @param name
     *            The name of the ship
     * @param health
     *            The health of the ship
     * @param defense
     *            The defense of the ship
     */
    public Ship(String name, int health, int defense) {
        this(name, health, defense, 0, 1, 1, 1, 1);
    }

    /**
     * Get's ship's health
     * 
     * @return the health
     */
    public int getHealth() {
        System.out.println(health);
        return health;
    }

    /**
     * Gets ship's maximum shield value
     * 
     * @return the max shield value
     */
    public int getShield() {
        return maxShields;
    }

    /**
     * Gets ship's weapon level
     * 
     * @return the weapon
     */
    public int getWeapon() {
        return weapons;
    }

    /**
     * Gets ship's armor level
     * 
     * @return the armor level
     */
    public int getArmor() {
        return armors;
    }

    /**
     * Gets ship's name
     * 
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * Gets the type of ship
     * 
     * @return the type
     */
    public String getType() {
        return type;
    }

    /**
     * Upgrades the ship's weapon level
     */
    public void upgradeWeapon() {
        weapons++;
    }

    /**
     * upgrades the ship's armor level
     */
    public void upgradeArmor() {
        armors++;
    }

    /**
     * upgrades the ship's shield level
     */
    public void upgradeShield() {
        maxShields++;
    }

    /**
     * sets the type of the ship
     * 
     * @param type
     *            type of the ship to set
     */
    public void setType(String type) {
        this.type = type;
    }

    /**
     * Removes health from the ship
     * 
     * @param value
     *            health to be removed
     * @return Whether health was removed.
     */
    public boolean removeHealth(int value) {
        if (health <= 0) {
            return false;
        }
        if (health - value <= 0) {
            health = 0;
            return true;
        } else {
            health -= value;
            return true;
        }
    }

    /**
     * Sets ship's health to max value (100)
     */
    public void heal() {
        final int maxHealth = 100;
        health = maxHealth;
    }

    /**
     * Calculates the Defense of the ship from base stats + armor upgrades
     * 
     * @param engineeringSkill
     *            Engineering skill used to calculate defense
     * @return total defense
     */
    public int calculateDefense(int engineeringSkill) {
        final int armorMod = 3;
        final int shiftMod = 1;
        final int defense = (shipDefense + armors * armorMod + (engineeringSkill >> shiftMod));
        //System.out.println("Defense: " + defense);
        return defense;
    }

    /**
     * Calculates attack
     * 
     * @param fighterSkill
     *            Fighter skill used for calculating attack
     * @return the attack value
     */
    public int calculateAttack(int fighterSkill) {
        final int weaponMod = 3;
        final int attackMod = 10;
        final int attack = weapons * weaponMod + attackMod
                + (int) (fighterSkill * Math.random());
        return attack;
    }

    /**
     * Takes damage after taking into account defense
     * 
     * @param rawDamage
     *            Raw damage value (aka Enemy Ship's attack)
     * @param engineerSkill
     * 			  the skill points allocated for engineering skill
     * @return Whether damage was taken
     * 
     */
    public int takeDamage(int rawDamage, int engineerSkill) {
        int damage = rawDamage - calculateDefense(engineerSkill);
        if (damage < 0)
            damage = 0;
        System.out.println(damage);
        if (shields == 0) {
            removeHealth(damage);
        } else {
            if (damage > shields) {
                final int leftoverDamage = damage - shields;
                shields = 0;
                removeHealth(leftoverDamage);
                return damage;
            } else {
                shields -= damage;
            }
        }

        return damage;
    }

    /**
     * Tells this ship to attack an enemy ship.
     * @param enemyShip		Ship you are firing upon.
     * @param fighterSkill	Fighter skill of player.
     * @param enemyEngineerSkill	Enemy engineering skill
     * @param enemyPilot	Enemy pilot you're attacking
     * @return				The damage you deal to the enemy ship, or -1 if no damage.
     */
    public int attack(Ship enemyShip, int fighterSkill, int enemyEngineerSkill,
            int enemyPilot) {
        double missChance = Math.random();
        missChance -= enemyPilot / ONEHUNDREDDOUBLE;
        if (missChance < MISS_RATE) {
            Debug.write("Missed shot.", Debug.EVENTS);
            return -1;
        }
        final int damageDealt = enemyShip.takeDamage(calculateAttack(fighterSkill),
                                                     enemyEngineerSkill);
        return damageDealt;
    }

    /**
     * Returns weather or not the ship can take off
     * @return Whether the ship can take off or not
     */
    public boolean canFly() {
        if (inventoryWeight > maxWeight) {
            return false;
        }
        return true;
    }

    /**
     * Returns the name of the ship as a string
     * 
     * @return The name of the ship in string format
     */
    public String toString() {
        return "Ship: " + name + " of type " + getClass().getCanonicalName();
    }

    /**
     * Adds weight to the cargo bay.
     * @param weight	Weight to add to cargo bay.
     */
    public void addWeight(int weight) {
        inventoryWeight += weight;
    }

    /**
     * Removes weight from the cargo bay.
     * @param weight	Weight to remove.
     */
    public void removeWeight(int weight) {
        inventoryWeight -= weight;
    }

    /**
     * Getter for MaxWeight.
     * @return	The Maximum weight that the ship's cargo bay can hold.
     */
    public int getMaxWeight() {
        return this.maxWeight;
    }

    /**
     * Getter for current weight.
     * @return	The weight of the cargo on the ship currently.
     */
    public int getCurrentWeight() {
        return this.inventoryWeight;
    }

    /**
     * Getter for max fuel.
     * @return	The size of the ship's fuel tank.
     */
    public int getMaxFuel() {
        return maxFuel;
    }

    /**
     * Getter for current fuel.
     * @return	How full the tank is.
     */
    public int getCurrentFuel() {
        return currentFuel;
    }

    /**
     * Attempts to use up fuel.
     * @param fuel	Amount being used up.
     * @return	True if you had enough, false otherwise.
     */
    public boolean useFuel(int fuel) {
        if (currentFuel - fuel >= 0) {
            currentFuel -= fuel;
            return true;
        } else {
            return false;
        }
    }

    /**
     * Refuels the ship's tank.
     * @param fuel	Amount of fuel to put in the tank.
     * @return	True if your tank was large enough, false otherwise.
     */
    public boolean addFuel(int fuel) {
        if (currentFuel + fuel > maxFuel) {
            return false;
        }
        currentFuel += fuel;
        return true;
    }

    /**
     * Tells whether the ship has enough room for a specific amount of resources.
     * @param res	Resource being added
     * @param amount	Number of res being added
     * @return	True if you have the cargo space, false otherwise.
     */
    public boolean canHold(Resource res, int amount) {
        if ((inventoryWeight + (res.weight() * amount)) > maxWeight) {
            return false;
        }
        return true;
    }

    /**
     * Returns the status of the ship - health and shields.
     * @return	Health is at health, shields are at current/total.
     */
    public String getStatus() {
        return new String("health is at " + health + "/100 and shields are at " + shields
                + "/" + maxShields + ".");
    }
    
    /**
     * Completely heals the ship and restores shields to default values. 
     */
    public void fullHeal(){
        health = 100;
        shields = maxShields;
    }
    
    /**
     * Manually sets the shields to a certain value
     * @param val Value of the shield to set
     */
    public void setShields(int val){
        shields = val;
    }
    
    public void calculateWeight(Inventory i){
    	this.inventoryWeight = 0;
    	for(Resource r : Resource.values()){
    		if(i.getResource(r) > 0){
    			inventoryWeight += (i.getResource(r) * r.weight());
    		}
    	}
    }

    public void upgradeMaxCargoWeight(){
        maxWeight += 500;
    }
    
    public int getMaxCargoWeight(){
        return maxWeight;
    }
}